﻿using TSU.FAMC.ComputerEngineering.Classes.Circuits.Events;
using TSU.FAMC.ComputerEngineering.Classes.Enums;

namespace TSU.FAMC.ComputerEngineering.Classes.Circuits
{
    /// <summary>
    /// Delegate for Signal events.
    /// </summary>
    /// <param name="sender">Sender object.</param>
    /// <param name="e">Event arguments.</param>
    public delegate void SignalEventHandler(object sender, SignalEventArgs e);

    /// <summary>
    /// Circuit's signal.
    /// </summary>
    public class Signal
    {
        // Properties

        /// <summary>
        /// A value of the signal.
        /// </summary>
        public BooleanValue Value { get; set; }


        // Methods

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Signal()
        {
            Value = BooleanValue.Undefined;
        }

        /// <summary>
        /// Invert signal.
        /// </summary>
        /// <param name="signal">Signal to invert.</param>
        /// <returns>Inverted signal.</returns>
        public static Signal operator ~(Signal signal)
        {
            if (signal.Value == BooleanValue.Undefined)
            {
                return signal;
            }

            var result = new Signal
                             {
                                 Value = signal.Value == BooleanValue.True ? BooleanValue.False : BooleanValue.True
                             };
            return result;
        }

        /// <summary>
        /// Performs logical AND operation with two Signal objects.
        /// </summary>
        /// <param name="s1">First signal.</param>
        /// <param name="s2">Second signal.</param>
        /// <returns>Operation result.</returns>
        public static Signal operator &(Signal s1, Signal s2)
        {
            var result = new Signal();
            if (s1.Value == BooleanValue.False || s2.Value == BooleanValue.False)
            {
                result.Value = BooleanValue.False;
            }
            else if (s1.Value == BooleanValue.True && s2.Value == BooleanValue.True)
            {
                result.Value = BooleanValue.True;
            }

            return result;
        }


        /// <summary>
        /// Performs logical OR operation with two Signal objects.
        /// </summary>
        /// <param name="s1">First signal.</param>
        /// <param name="s2">Second signal.</param>
        /// <returns>Operation result.</returns>
        public static Signal operator |(Signal s1, Signal s2)
        {
            var result = new Signal();
            if (s1.Value == BooleanValue.True || s2.Value == BooleanValue.True)
            {
                result.Value = BooleanValue.True;
            }
            else if (s1.Value == BooleanValue.False && s2.Value == BooleanValue.False)
            {
                result.Value = BooleanValue.False;
            }

            return result;
        }

        /// <summary>
        /// Performs logical XOR operation with two Signal objects.
        /// </summary>
        /// <param name="s1">First signal.</param>
        /// <param name="s2">Second signal.</param>
        /// <returns>Operation result.</returns>
        public static Signal operator ^(Signal s1, Signal s2)
        {
            var result = new Signal();
            if (s1.Value != BooleanValue.Undefined && s1.Value == s2.Value)
            {
                result.Value = BooleanValue.False;
            }
            else if (s1.Value != BooleanValue.Undefined && s2.Value != BooleanValue.Undefined && s1.Value != s2.Value)
            {
                result.Value = BooleanValue.True;
            }

            return result;
        }

        /// <summary>
        /// Determines whether specified object is equal to the current object.
        /// </summary>
        /// <param name="obj">An object to compare with.</param>
        /// <returns>True if objects are equal.</returns>
        public override bool Equals(object obj)
        {
            if (obj is Signal)
            {
                return Equals(obj as Signal);
            }

            return base.Equals(obj);
        }

        /// <summary>
        /// Checks whether two signals are equal.
        /// </summary>
        /// <param name="signal">Sinal object to compare with.</param>
        /// <returns>True if signals are equal (int terms of their values).</returns>
        public bool Equals(Signal signal)
        {
            if (ReferenceEquals(null, signal)) return false;
            return ReferenceEquals(this, signal) ||
                  (signal.Value != BooleanValue.Undefined &&
                   Equals(signal.Value, Value));
        }

        /// <summary>
        /// Checks value equality of two Signal objects.
        /// </summary>
        /// <param name="s1">First signal.</param>
        /// <param name="s2">Second signal.</param>
        /// <returns>True if signals are equal in terms of their values.</returns>
        public static bool operator ==(Signal s1, Signal s2)
        {
            return s1.Equals(s2);
        }

        /// <summary>
        /// Checks value inequality of two signal objects.
        /// </summary>
        /// <param name="s1">First signal.</param>
        /// <param name="s2">Second signal.</param>
        /// <returns>True if signals are not equal in terms of their values.</returns>
        public static bool operator !=(Signal s1, Signal s2)
        {
            return !(s1 == s2);
        }

        /// <summary>
        /// Returns hash code.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}
